package com.lyEducation.repositoryImpl;

import com.lyEducation.core.base.Pageable;
import com.lyEducation.core.base.ProxyEntityManager;
import com.lyEducation.core.base.SysConstants;
import com.lyEducation.entities.base.Page;
import com.lyEducation.repository.AbstractPersisTableRepository;
import com.lyEducation.util.EntityUtil;
import org.hibernate.query.NativeQuery;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.transform.Transformers;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.*;

@Transactional(rollbackFor = Exception.class, readOnly = false, propagation = Propagation.REQUIRED)
public abstract class AbstractPersisTableRepositoryImpl<I>
        implements AbstractPersisTableRepository<I>, ProxyEntityManager {
  //  private final String primaryKeyName1 = "primaryKeyName1";
  //  private final String primaryKeyName2 = "primaryKeyName2";

  @PersistenceContext protected EntityManager entityManager;

  @Override
  public EntityManager getEntityManager() {
    return entityManager;
  }

  protected void setEntityManager(EntityManager entityManager) {
    this.entityManager = entityManager;
  }

  @Override
  public <T> T save(T entity) {
    entityManager.persist(entity);
    return entity;
  }

  @Override
  public <T> T update(T entity) {
    return entityManager.merge(entity);
  }

  @Override
  public <T> T findById(Class<T> clazz, I id) {
    return entityManager.find(clazz, id);
  }

  @Override
  public <T> List<T> findByFiled(Class<T> clazz, String filed, Object o) {
    String sql = "from " + EntityUtil.getHqlTableName(clazz) + " u WHERE u." + filed + "=:" + filed;
    Query query = entityManager.createQuery(sql);
    query.setParameter(filed, o);
    return query.getResultList();
  }

  @Override
  public <T> List<T> findByFileds(Class<T> clazz, LinkedHashMap<String, Object> linkedHashMap) {

    String sql = "from " + EntityUtil.getHqlTableName(clazz) + " u WHERE ";
    Set<String> linkedKeySet = linkedHashMap.keySet();
    Collection collectionValues = linkedHashMap.values();

    for (String filed : linkedKeySet) {
      sql += "u." + filed + "=? and ";
    }
    sql = sql.substring(0, sql.length() - 4);
    Query query = entityManager.createQuery(sql);

    Iterator<Object> iterator = collectionValues.iterator();
    int i = 0;
    while (iterator.hasNext()) {
      query.setParameter(i, iterator.next());
      i++;
    }

    return query.getResultList();
  }

  @Override
  public <T> List<T> findAll(Class<T> clazz) {
    String hql = " from " + EntityUtil.getHqlTableName(clazz) + " u ";
    Query query = entityManager.createQuery(hql);
    return query.getResultList();
  }

  @Override
  public <T> List<T> findByNativeQuery(
          String sql, Map<String, Object> conditionParams, ResultTransformer resultTransformer) {

    Query nativeQuery = entityManager.createNativeQuery(sql);
    if (conditionParams != null) {
      for (String paramKey : conditionParams.keySet()) {
        nativeQuery.setParameter(paramKey, conditionParams.get(paramKey));
      }
    }
    if (resultTransformer != null) {
      nativeQuery.unwrap(NativeQuery.class).setResultTransformer(resultTransformer);
    }
    List<T> list = nativeQuery.getResultList();
    return list;
  }

  @Override
  public <T> List<T> findPageByNativeQueryFiled(
          Class<T> clazz, String filed, Object o, int pageNo, int pageSize) {
    String sql = "from " + EntityUtil.getHqlTableName(clazz) + " u WHERE u." + filed + "=?";
    List<T> list = new ArrayList<>();
    //    try {
    Query query = entityManager.createQuery(sql);
    query.setParameter(1, o);
    query.setFirstResult((pageNo - 1) * pageSize);
    query.setMaxResults(pageSize);
    list = query.getResultList();
    //    } catch (Exception e) {
    //      e.printStackTrace();
    //    } finally {
    //      entityManager.close();
    //    }
    return list;
  }

  @Override
  public Map<String, Object> findPageByNativeQueryFileds(
          String sql,
          Map<String, Object> conditionParams,
          ResultTransformer resultTransformer,
          Integer pageNo,
          Integer pageSize) {

    String countSql = "select count(1) from (" + sql + ") u";
    Query nativeQuery = entityManager.createNativeQuery(sql);
    Query nativeCountQuery = entityManager.createNativeQuery(countSql);
    for (String paramKey : conditionParams.keySet()) {
      nativeCountQuery.setParameter(paramKey, conditionParams.get(paramKey));
      nativeQuery.setParameter(paramKey, conditionParams.get(paramKey));
    }
    List totalList = nativeCountQuery.getResultList();
    BigInteger total = (BigInteger) totalList.get(0);
    if (pageNo != null) {
      nativeQuery.setFirstResult((pageNo - 1) * pageSize);
    }
    if (pageSize != null) {
      nativeQuery.setMaxResults(pageSize);
    }
    if (resultTransformer != null) {
      nativeQuery.unwrap(NativeQuery.class).setResultTransformer(resultTransformer);
    }
    List rows = nativeQuery.getResultList();
    Map<String, Object> resultMap = new HashMap<>(2);
    resultMap.put(SysConstants.ROWS.getValue(), rows);
    resultMap.put(SysConstants.TOTAL.getValue(), total);
    return resultMap;
  }

  @Override
  public <T> Integer updateFileds(Class<T> clazz, String hql,
                                  Map<String, Object> conditionParams, Map<String, Object> valueParams) {
//    ameter(key, map.get(key));
    //    }
    //    query.setParameter(idKey, idValue);    StringBuffer sql = new StringBuffer();
    ////    sql.append("UPDATE ").append(EntityUtil.getHqlTableName(clazz)).append(" AS u SET ");
    ////
    ////    Set<String> set = map.keySet();
    ////    String idKey = "id";
    ////    String idValue = map.get(idKey).toString();
    ////    map.remove(idKey);
    ////    Iterator<String> iterator = set.iterator();
    ////    while (iterator.hasNext()) {
    ////      String key = iterator.next();
    ////      sql.append("u.").append(key).append("=:").append(key);
    ////      if (iterator.hasNext()) {
    ////        sql.append(",");
    ////      }
    ////    }
    ////    sql.append(" WHERE u.id=:id");
    ////    Query query = entityManager.createQuery(sql.toString());
    ////    for (String key : map.keySet()) {
    ////      query.setParameter(key, map.get(key));
    ////    }
    ////    query.setParameter(idKey, idValue);
//    int resurlt = query.executeUpdate();
    if (clazz != null) {
      StringBuffer clazzNotNullSql = new StringBuffer();
      clazzNotNullSql.append("UPDATE ").append(EntityUtil.getHqlTableName(clazz)).append(" ").append(hql);
      hql = clazzNotNullSql.toString();
    }
    Query query = entityManager.createQuery(hql);
    if (conditionParams != null) {
      for (String paramKey : conditionParams.keySet()) {
        query.setParameter(paramKey, conditionParams.get(paramKey));
      }
    }
    if (valueParams != null) {
      for (String paramKey : valueParams.keySet()) {
        query.setParameter(paramKey, valueParams.get(paramKey));
      }
    }
    return query.executeUpdate();
  }

  @Override
  public <T> boolean remove(T entity) {
    boolean flag = false;
    entityManager.remove(entityManager.merge(entity));
    flag = true;
    return flag;
  }

  @Override
  public <T> boolean deleteById(Class<T> clazz, I id) {
    String hql = "delete from " + EntityUtil.getHqlTableName(clazz) + " u WHERE u.id = " + id + "";
    Query query = entityManager.createQuery(hql);
    if (query.executeUpdate() > 0) {
      return true;
    } else {
      return false;
    }
  }

  @Override
  public <T> boolean deleteByMap(Class<T> clazz, Map paramsMap) {
    String hql = "delete from " + EntityUtil.getHqlTableName(clazz) + " u WHERE ";
    if (paramsMap == null || paramsMap.isEmpty()) {
      return false;
    }
    for (Object key : paramsMap.keySet()) {
      if (paramsMap.get(key).getClass().getTypeName().equals("java.lang.String")) {
        hql += " u." + key + "='" + paramsMap.get(key) + "' AND ";
      } else {
        hql += " u." + key + "=" + paramsMap.get(key) + " AND ";
      }
    }
    hql = hql.substring(0, hql.length() - 4);
    Query query = entityManager.createQuery(hql);
    if (query.executeUpdate() > 0) {
      return true;
    } else {
      return false;
    }
  }

  //  Query nativeQuery = entityManager.createNativeQuery(sql);
  //    if (conditionParams != null) {
  //    for (String paramKey : conditionParams.keySet()) {
  //      nativeQuery.setParameter(paramKey, conditionParams.get(paramKey));
  //    }
  //  }
  //    if (resultTransformer != null) {
  //    nativeQuery.unwrap(NativeQuery.class).setResultTransformer(resultTransformer);
  //  }
  //  List<T> list = nativeQuery.getResultList();
  //    return list;
  @Override
  public <T> boolean deleteByMap(Class<T> clazz, String hql, Map<String, Object> paramsMap) {
    Query query = entityManager.createQuery(hql);
    for (Map.Entry<String, Object> entry : paramsMap.entrySet()) {
      query.setParameter(entry.getKey(), entry.getValue());
    }
    if (query.executeUpdate() > 0) {
      return true;
    } else {
      return false;
    }
  }

  @Override
  public <T> T findByIdClass(T entity, Object id) {
    return (T) entityManager.find(entity.getClass(), id);
  }

  @Override
  public <MT> boolean addMiddleTable(
          Class<MT> clazz,
          String primaryKeyName1,
          String primaryKeyValue1,
          String primaryKeyName2,
          String primaryKeyValue2,
          Timestamp createTime) {
    String sql =
            "INSERT INTO "
                    + EntityUtil.getSqlTableName(clazz)
                    + " (`roleId`, `userId`, `createTime`) VALUES (:roleId,:userId,:createTime);";
    Map paramMap = new HashMap();
    paramMap.put(primaryKeyName1, primaryKeyValue1);
    paramMap.put(primaryKeyName2, primaryKeyValue2);
    List<MT> resList = findByNativeQuery(sql, paramMap, Transformers.aliasToBean(clazz));

    return false;
  }

  @Override
  public <MT> MT findMiddleTable(
          Class<MT> clazz,
          String primaryKeyName1,
          String primaryKeyValue1,
          String primaryKeyName2,
          String primaryKeyValue2) {
    String sql =
            "select * from "
                    + EntityUtil.getSqlTableName(clazz)
                    + " where "
                    + primaryKeyName1
                    + "=:"
                    + primaryKeyName1
                    + " and "
                    + primaryKeyName2
                    + "=:"
                    + primaryKeyName2;
    Map paramMap = new HashMap();
    paramMap.put(primaryKeyName1, primaryKeyValue1);
    paramMap.put(primaryKeyName2, primaryKeyValue2);
    //    List<MT> resList = entityManager.createQuery(sql, paramMap,
    // Transformers.aliasToBean(clazz));
    //    if (resList.size() != 0) {
    //      return resList.get(0);
    //    }
    List<MT> list = this.findByNativeQuery(sql, paramMap, Transformers.aliasToBean(clazz));
    if (list.size() > 1) {
      throw new RuntimeException("queryMiddle error");
    }
    if (list.size() == 0) {
      return null;
    }
    return (MT) list.get(0);
  }

  @Override
  public <MT> boolean delMiddleTable(
          Class<MT> clazz,
          String primaryKeyName1,
          String primaryKeyValue1,
          String primaryKeyName2,
          String primaryKeyValue2) {
    String sql =
            "DELETE FROM "
                    + EntityUtil.getSqlTableName(clazz)
                    + " where "
                    + primaryKeyName1
                    + "=:"
                    + primaryKeyName1;
    Map paramMap = new HashMap();
    paramMap.put(primaryKeyName1, primaryKeyValue1);
    if (primaryKeyName2 != null && primaryKeyValue2 != null) {
      sql += " and " + primaryKeyName2 + "=:" + primaryKeyName2;
      paramMap.put(primaryKeyName2, primaryKeyValue2);
    }
    return this.deleteByMap(clazz, paramMap);
  }

  @Override
  public <MT> boolean updMiddleTable(
          Class<MT> clazz, String name1, String value1, String name2, String value2) {
    return false;
  }

  @Override
  public <I> List<I> resultList(Class<I> iClass, String ql, Iterable<Object> parameters) {
    TypedQuery<I> query = entityManager.createQuery(ql, iClass);
    query(query, parameters);
    return query.getResultList();
  }

  @Override
  @SuppressWarnings("unchecked")
  public List<Object> resultList(String ql, Iterable<Object> parameters) {
    Query query = entityManager.createQuery(ql);
    query(query, parameters);
    return query.getResultList();
  }

  @Override
  @SuppressWarnings("unchecked")
  public Page resultPage(String sql, String cql, Pageable pageable, Iterable<Object> parameters) {
    Query query = entityManager.createQuery(sql), count = entityManager.createQuery(cql);
    int i = 1;
    for (Object par : parameters) {
      count.setParameter(i, par);
      query.setParameter(i++, par);
    }
    query.setFirstResult(pageable.getFirstItem());
    query.setMaxResults(pageable.getPageSize());
    return new Page(((Number) count.getSingleResult()).longValue(), query.getResultList());
  }

  protected void query(Query query, Iterable<Object> parameters) {
    int i = 1;
    for (Object par : parameters) {
      query.setParameter(i++, par);
    }
  }

  protected <T> TypedQuery<T> query(Class<T> clazz, String hql) {
    return entityManager.createQuery(hql, clazz);
  }

  protected <T> TypedQuery<T> query(Class<T> clazz, String hql, Object arg) {
    TypedQuery<T> tqSelect = entityManager.createQuery(hql, clazz);
    return tqSelect.setParameter(1, arg);
  }

  protected <T> TypedQuery<T> query(Class<T> clazz, String hql, Object arg, Object arg2) {
    TypedQuery<T> tqSelect = entityManager.createQuery(hql, clazz);
    return tqSelect.setParameter(1, arg).setParameter(2, arg2);
  }

  protected <T> TypedQuery<T> query(Class<T> clazz, String hql, Object... args) {
    TypedQuery<T> tqSelect = entityManager.createQuery(hql, clazz);
    for (int i = 0, l = args.length; i < l; i++) {
      tqSelect.setParameter(i + 1, args[i]);
    }
    return tqSelect;
  }

  // ---------------------------------exsit
  protected boolean exsit(String ql) {
    return exsit(query(Number.class, ql));
  }

  protected boolean exsit(String ql, Object arg) {
    return exsit(query(Number.class, ql, arg));
  }

  protected boolean exsit(String ql, Object arg, Object arg2) {
    return exsit(query(Number.class, ql, arg, arg2));
  }

  protected boolean exsit(String ql, Object... args) {
    return exsit(query(Number.class, ql, args));
  }

  protected boolean exsit(TypedQuery<Number> t) {
    return t.getSingleResult().intValue() > 0;
  }

  // ---------------------------------first
  @Override
  public <T> T first(TypedQuery<T> tqSelect) {
    List<T> items = tqSelect.setMaxResults(1).getResultList();
    return items.isEmpty() ? null : items.get(0);
  }

  @Override
  public <T> T first(Class<T> clazz, String hql) {
    return first(query(clazz, hql));
  }

  @Override
  public <T> T first(Class<T> clazz, String hql, Object arg) {
    return first(query(clazz, hql, arg));
  }

  @Override
  public <T> T first(Class<T> clazz, String hql, Object arg, Object arg2) {
    return first(query(clazz, hql, arg, arg2));
  }

  @Override
  public <T> T first(Class<T> clazz, String hql, Object... args) {
    return first(query(clazz, hql, args));
  }

  // ---------------------------------single
  @Override
  public <T> T single(Class<T> clazz, String hql) {
    return query(clazz, hql).getSingleResult();
  }

  @Override
  public <T> T single(Class<T> clazz, String hql, Object arg) {
    return query(clazz, hql, arg).getSingleResult();
  }

  @Override
  public <T> T single(Class<T> clazz, String hql, Object arg, Object arg2) {
    return query(clazz, hql, arg, arg2).getSingleResult();
  }

  @Override
  public <T> T single(Class<T> clazz, String hql, Object... args) {
    return query(clazz, hql, args).getSingleResult();
  }

  // ---------------------------------list
  @Override
  public <T> List<T> list(Class<T> clazz, String hql) {
    return query(clazz, hql).getResultList();
  }

  @Override
  public <T> List<T> list(Class<T> clazz, String hql, Object arg) {
    return query(clazz, hql, arg).getResultList();
  }

  @Override
  public <T> List<T> list(Class<T> clazz, String hql, Object arg, Object arg2) {
    return query(clazz, hql, arg, arg2).getResultList();
  }

  @Override
  public <T> List<T> list(Class<T> clazz, String hql, Object... args) {
    return query(clazz, hql, args).getResultList();
  }

  // ---------------------------------top
  @Override
  public <T> List<T> top(int top, Class<T> clazz, String hql) {
    return query(clazz, hql).setMaxResults(top).getResultList();
  }

  @Override
  public <T> List<T> top(int top, Class<T> clazz, String hql, Object arg) {
    return query(clazz, hql, arg).setMaxResults(top).getResultList();
  }

  @Override
  public <T> List<T> top(int top, Class<T> clazz, String hql, Object arg, Object arg2) {
    return query(clazz, hql, arg, arg2).setMaxResults(top).getResultList();
  }

  @Override
  public <T> List<T> top(int top, Class<T> clazz, String hql, Object... args) {
    return query(clazz, hql, args).setMaxResults(top).getResultList();
  }

  // ---------------------------------exec
  protected int exec(String ql) {
    return entityManager.createQuery(ql).executeUpdate();
  }

  protected int exec(String ql, Object arg) {
    return entityManager.createQuery(ql).setParameter(1, arg).executeUpdate();
  }

  protected int exec(String ql, Object arg, Object arg2) {
    return entityManager.createQuery(ql).setParameter(1, arg).setParameter(2, arg2).executeUpdate();
  }

  protected int exec(String ql, Object arg, Object arg2, Object arg3) {
    return entityManager
            .createQuery(ql)
            .setParameter(1, arg)
            .setParameter(2, arg2)
            .setParameter(3, arg3)
            .executeUpdate();
  }
}
